package com.example.server.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.example.dao.menuMapper.MenuMapper;
import com.example.dto.dept.Dept;
import com.example.dto.menu.Menu;
import com.example.dto.menu.MenuCriteria;
import com.example.dto.role.Role;
import com.example.dto.vo.MenuMetaVo;
import com.example.dto.vo.MenuVo;
import com.example.exception.BadRequestException;
import com.example.exception.EntityExistException;
import com.example.server.MenuServer;
import com.example.server.RoleServer;
import com.example.util.ValidationUtil;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@CacheConfig(cacheNames = "menu")
public class MenuServerImpl implements MenuServer {
    @Autowired
    private MenuMapper menuMapper;
    @Autowired
    private RoleServer roleServer;

    private static final String HTTP_PRE = "http://";
    private static final String HTTPS_PRE = "https://";
    private static final String YES_STR = "是";
    private static final String NO_STR = "否";
    private static final String BAD_REQUEST = "外链必须以http://或者https://开头";

    /**
     * 增加菜单
     *
     * @param menu
     * @return Map<String, Object>
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(Menu resources) {
        if (menuMapper.findByTitle(resources.getTitle()) != null) {
            throw new EntityExistException(Menu.class, "title", resources.getTitle());
        }
        if (StringUtils.isNotBlank(resources.getComponentName())) {
            if (menuMapper.findByComponentName(resources.getComponentName()) != null) {
                throw new EntityExistException(Menu.class, "componentName", resources.getComponentName());
            }
        }
        if (Long.valueOf(0L).equals(resources.getPid())) {
            resources.setPid(null);
        }
        if (resources.getIFrame()) {
            if (!(resources.getPath().toLowerCase().startsWith(HTTP_PRE) || resources.getPath().toLowerCase().startsWith(HTTPS_PRE))) {
                throw new BadRequestException(BAD_REQUEST);
            }
        }
        menuMapper.creat(resources);
        /* 计算子节点数目 */
        resources.setSubCount(0);
        // 更新父节点菜单数目
        updateSubCnt(resources.getPid());
    }

    /**
     * 修改菜单信息
     *
     * @param
     * @return Map<String, Object>
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(Menu resources) {
        if (resources.getId().equals(resources.getPid())) {
            throw new BadRequestException("上级不能为自己");
        }
        Menu menu = menuMapper.findByMenusId(resources.getId());
        ValidationUtil.isNull(menu.getId(), "Permission", "id", resources.getId());

        if (resources.getIFrame()) {
            if (!(resources.getPath().toLowerCase().startsWith(HTTP_PRE) || resources.getPath().toLowerCase().startsWith(HTTPS_PRE))) {
                throw new BadRequestException(BAD_REQUEST);
            }
        }
        Menu menu1 = menuMapper.findByTitles(resources.getTitle());

        if (menu1 != null && !menu1.getId().equals(menu.getId())) {
            throw new EntityExistException(Menu.class, "title", resources.getTitle());
        }

        if (resources.getPid().equals(0L)) {
            resources.setPid(null);
        }

        // 记录的父节点ID
        Long oldPid = menu.getPid();
        Long newPid = resources.getPid();

        if (StringUtils.isNotBlank(resources.getComponentName())) {
            menu1 = menuMapper.findByComponentName(resources.getComponentName());
            if (menu1 != null && !menu1.getId().equals(menu.getId())) {
                throw new EntityExistException(Menu.class, "componentName", resources.getComponentName());
            }
        }
        menu.setTitle(resources.getTitle());
        menu.setComponent(resources.getComponent());
        menu.setPath(resources.getPath());
        menu.setIcon(resources.getIcon());
        menu.setIFrame(resources.getIFrame());
        menu.setPid(resources.getPid());
        menu.setMenuSort(resources.getMenuSort());
        menu.setCache(resources.getCache());
        menu.setHidden(resources.getHidden());
        menu.setComponentName(resources.getComponentName());
        menu.setPermission(resources.getPermission());
        menu.setType(resources.getType());
        menuMapper.save(menu);
        // 计算父级菜单节点数目
        updateSubCnt(oldPid);
        updateSubCnt(newPid);
    }

    private void updateSubCnt(Long menuId) {
        if (menuId != null) {
            int count = menuMapper.countByPid(menuId);
            menuMapper.updateSubCntById(count, menuId);
        }
    }

    /**
     * 删除菜单
     *
     * @param ids
     * @return Map<String, Object>
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(List<Long> ids) {
        menuMapper.deleteById(ids);
    }

    /**
     * 查询菜单
     *
     * @param menuCriteria
     * @return Map<String, Object>
     */

    //获取全部菜单
    @Override
    public List<Menu> list(MenuCriteria menuCriteria) {
        //查询菜单所有数据
        Map<String, Object> result = new HashMap<>(4);

        Map<String, Object> hash = new HashMap<>();
        hash.put("createTime",null);
        hash.put("updataeTime", null);
        hash.put("blurry", menuCriteria.getBlurry());
        hash.put("pid", menuCriteria.getPid());

        List<Menu> menus = menuMapper.selectList(hash);
        int count = menuMapper.findCount(hash);

        List<Menu> results = buildTrees(menus);
        //向results集合中的每个menu对象中设置hasChildren
        results.forEach(x -> {
            Long menuId = x.getId();
            boolean isChild = this.isChildren(menuId);
            x.setHasChildren(isChild);
        });
        return results;
    }

    private boolean isChildren(Long id) {
        int count = menuMapper.findCounts(id);
        return count > 0;
    }

    public List<Menu> buildTrees(List<Menu> menuDtos) {
        List<Menu> trees = new ArrayList<>();
        Set<Long> ids = new HashSet<>();
        for (Menu menuDTO : menuDtos) {
            if (menuDTO.getPid() == null) {
                trees.add(menuDTO);
            }
            for (Menu it : menuDtos) {
                if (menuDTO.getId().equals(it.getPid())) {
                    if (menuDTO.getChildren() == null) {
                        menuDTO.setChildren(new ArrayList<>());
                    }
//                    menuDTO.getChildren().add(it);
//                    ids.add(it.getId());
                }
            }
        }
        if (trees.size() == 0) {
            trees = menuDtos.stream().filter(s -> !ids.contains(s.getId())).collect(Collectors.toList());
        }
        return trees;
    }

    @Override
    public List<Menu> buildTree(List<Menu> menuDtos) {
        List<Menu> trees = new ArrayList<>();
        Set<Long> ids = new HashSet<>();
        for (Menu menuDTO : menuDtos) {
            if (menuDTO.getPid() == null) {
                trees.add(menuDTO);
            }
            for (Menu it : menuDtos) {
                if (menuDTO.getId().equals(it.getPid())) {
                    if (menuDTO.getChildren() == null) {
                        menuDTO.setChildren(new ArrayList<>());
                    }
                    menuDTO.getChildren().add(it);
                    ids.add(it.getId());
                }
            }
        }
        if (trees.size() == 0) {
            trees = menuDtos.stream().filter(s -> !ids.contains(s.getId())).collect(Collectors.toList());
        }
        return trees;
    }

    @Override
    public List<Menu> getMenus(Long pid) {
        List<Menu> menus;
        if (pid != null && !pid.equals(0L)) {
            menus = menuMapper.findByPidOrderByMenuSort(pid);
            menus.forEach(x -> {
                Long roleId = x.getId();
                List<Menu> menu = menuMapper.findByID(x.getId());
                x.setLabel(x.getTitle());
            });
        } else {
            menus = menuMapper.findByPidIsNullOrderByMenuSort();
            menus.forEach(x -> {
                Long roleId = x.getId();
                List<Menu> menu = menuMapper.findByID(x.getId());
                x.setLabel(x.getTitle());
            });
        }
        return menus;
    }

    @Override
    public Menu findOne(Long id) {
        Menu menu = menuMapper.findByMenusId(id);
        ValidationUtil.isNull(menu.getId(), "Menu", "id", id);
        return menu;
    }

    @Override
    public Set<Menu> getChildMenus(List<Menu> menuList, Set<Menu> menuSet) {
        for (Menu menu : menuList) {
            menuSet.add(menu);
            List<Menu> menus = menuMapper.findByPidOrderByMenuSort(menu.getId());
            if (menus != null && menus.size() != 0) {
                getChildMenus(menus, menuSet);
            }
        }
        return menuSet;
    }

    @Override
    public Menu findById(long id) {
        Menu menu = menuMapper.findByMenusId(id);
        menu.setLabel(menu.getTitle());
        ValidationUtil.isNull(menu.getId(), "Menu", "id", id);
        return menu;
    }

    @Override
    public List<Menu> getSuperior(Menu menu, List<Menu> menus) {
        if (menu.getPid() == null) {
            menus.addAll(menuMapper.findByPidIsNullOrderByMenuSort());
            menus.forEach(x -> {
                x.setLabel(x.getTitle());
            });
            return menus;
        }
        menus.addAll(menuMapper.findByPidOrderByMenuSort(menu.getPid()));
        return getSuperior(findById(menu.getPid()), menus);
    }

    @Override
    public List<Menu> findByUser(Long userId) {
        List<Role> roles = roleServer.findByUsersId(userId);
        Set<Long> roleIds = roles.stream().map(Role::getId).collect(Collectors.toSet());
        LinkedHashSet<Menu> menus = menuMapper.findByRoleIdsAndTypeNot(roleIds, 2);
        return menus.stream().collect(Collectors.toList());
    }

    @Override
    public List<MenuVo> buildMenus(List<Menu> menus) {
        List<MenuVo> list = new LinkedList<>();
        menus.forEach(menuDTO -> {
                    if (menuDTO != null) {
                        List<Menu> menuDtoList = menuDTO.getChildren();
                        MenuVo menuVo = new MenuVo();
                        menuVo.setName(ObjectUtil.isNotEmpty(menuDTO.getComponentName()) ? menuDTO.getComponentName() : menuDTO.getTitle());
                        // 一级目录需要加斜杠，不然会报警告
                        menuVo.setPath(menuDTO.getPid() == null ? "/" + menuDTO.getPath() : menuDTO.getPath());
                        menuVo.setHidden(menuDTO.getHidden());
                        // 如果不是外链
                        if (!menuDTO.getIFrame()) {
                            if (menuDTO.getPid() == null) {
                                menuVo.setComponent(StringUtils.isEmpty(menuDTO.getComponent()) ? "Layout" : menuDTO.getComponent());
                                // 如果不是一级菜单，并且菜单类型为目录，则代表是多级菜单
                            } else if (menuDTO.getType() == 0) {
                                menuVo.setComponent(StringUtils.isEmpty(menuDTO.getComponent()) ? "ParentView" : menuDTO.getComponent());
                            } else if (StringUtils.isNoneBlank(menuDTO.getComponent())) {
                                menuVo.setComponent(menuDTO.getComponent());
                            }
                        }
                        menuVo.setMeta(new MenuMetaVo(menuDTO.getTitle(), menuDTO.getIcon(), !menuDTO.getCache()));
                        if (CollectionUtil.isNotEmpty(menuDtoList)) {
                            menuVo.setAlwaysShow(true);
                            menuVo.setRedirect("noredirect");
                            menuVo.setChildren(buildMenus(menuDtoList));
                            // 处理是一级菜单并且没有子菜单的情况
                        } else if (menuDTO.getPid() == null) {
                            MenuVo menuVo1 = new MenuVo();
                            menuVo1.setMeta(menuVo.getMeta());
                            // 非外链
                            if (!menuDTO.getIFrame()) {
                                menuVo1.setPath("index");
                                menuVo1.setName(menuVo.getName());
                                menuVo1.setComponent(menuVo.getComponent());
                            } else {
                                menuVo1.setPath(menuDTO.getPath());
                            }
                            menuVo.setName(null);
                            menuVo.setMeta(null);
                            menuVo.setComponent("Layout");
                            List<MenuVo> list1 = new ArrayList<>();
                            list1.add(menuVo1);
                            menuVo.setChildren(list1);
                        }
                        list.add(menuVo);
                    }
                }
        );
        return list;
    }
}
